feat(apl): generic-HTTP authorization#110
Open
araujof wants to merge 3 commits into
Open
Conversation
Add optional method/path/host/scheme to HttpExtension and surface them in the APL bag as http.method/path/host/scheme. These let CEL/APL policies reason over the HTTP request line — needed by the Praxis AuthPolicy transpiler, where Kuadrant predicates over request.method/path/host map to http.* (Praxis spike Phase B / U1). The request line rides the existing read_headers capability: the `http` extension slot is gated as a whole in cpex-core's filter_extensions, so a base-tier split would require granular http sub-field filtering (deferred). The host field is documented to be populated from a validated authority (e.g. HTTP/2 :authority), never a raw client Host header, so host-based policy cannot be bypassed. Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Add a per-route `response:` block (the transpiled form of a Kuadrant AuthPolicy `denyWith`) that lets a route declare a custom HTTP status, body, and headers for its denials (Praxis spike Phase B / U2). - New optional DenyResponse on CompiledRoute (additive; most-specific layer wins in apply_layer). Read out-of-band from the route YAML by the apl-cpex visitor, like the `policy:` block — cpex-core tolerates the key. - On Decision::Deny, route_handler stashes status/body/headers into the existing PluginViolation.details map under http.status / http.body / http.headers. No new fields on PluginViolation and no new APL grammar — the violation type stays stable and reason-only denies are unchanged. A host (e.g. the Praxis policy filter) reads details to render a custom denial response; absent → host default behavior. Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Make the catch-all `global` policy enforce on generic (non-MCP/A2A) HTTP
requests, which carry no entity (Praxis spike Phase B / U3).
- New reserved coordinates: ENTITY_HTTP ("http") / ENTITY_NAME_GLOBAL ("*")
and the HOOK_CMF_HTTP_REQUEST ("cmf.http_request") hook.
- The visitor installs a Pre-phase AplRouteHandler bound to the compiled
global policy under those coordinates, granted read_headers so the
policy can read the request line/headers. Entity routes still stack
`global` via apply_layer; this adds the entity-less evaluation path.
- A global-scope `response:` block (transpiled denyWith) is carried onto
the global handler and surfaced on deny via PluginViolation.details (U2).
A host fires invoke_named::<CmfHook>("cmf.http_request", ...) with
meta.entity_type/name set to the reserved coordinates. End-to-end tests
cover allow, deny, and custom-denyWith — exercising U1 + U2 + U3 together.
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Engine-side additions that let CPEX authorize generic (non-MCP/A2A) HTTP requests. These are consumed by the Praxis
policyfilter's new experimentalenforcement: httpmode to run Kuadrant-AuthPolicy-style authorization (see the companion Praxis PR). All changes are additive and non-breaking — existing MCP/entity behavior is unchanged.Draft: pairs with the Praxis AuthPolicy spike praxis-proxy/praxis#746. Cut a release from this branch before Praxis pins it (Praxis currently consumes it via a temporary path dep).
What's here
HttpExtension. Optionalmethod/path/host/scheme, surfaced in the APL bag ashttp.method/http.path/http.host/http.scheme. Ride the existingread_headerscapability (thehttpslot is gated as a whole infilter_extensions; a base-tier split would need granular sub-field filtering — deferred).hostis documented to come from a validated authority, never a raw clientHost.PluginViolation.details. A per-routeresponse:block (transpileddenyWith) is carried onCompiledRoute(additive; most-specific layer wins) and, onDecision::Deny, stashed into the existingdetailsmap underhttp.status/http.body/http.headers. No new fields onPluginViolation, no new APL grammar — reason-only denies are unchanged.globalpolicy for entity-less requests. New reserved coordinatesENTITY_HTTP/ENTITY_NAME_GLOBALand thecmf.http_requesthook. The visitor installs a Pre-phase handler bound to the compiledglobalpolicy under those coordinates (grantedread_headers), so a host can authorize a request that carries no MCP entity. Entity routes still stackglobalviaapply_layer.Tests
apl-cmf: request-line surfacing in the bag.apl-core:DenyResponseapply_layermerge.apl-cpex:response:sub-block parsing; and a new end-to-endglobal_http_authztest exercising everything together — allow, deny, and customdenyWithfor an entity-less HTTP request.Full
apl-core/apl-cpex/cpex-coresuites green; clippy + fmt clean.